/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-05
 * V4.0
 */
package com.jphenix.standard.db;

import com.jphenix.driver.serialize.ISerializable;
import com.jphenix.driver.threadpool.ThreadSession;
import com.jphenix.share.lang.SBoolean;
import com.jphenix.share.lang.SInteger;
import com.jphenix.share.lang.SString;
import com.jphenix.share.util.DebugUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.servlet.IActionContext;
import com.jphenix.standard.servlet.IServletConst;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 分页查询容器
 * 
 * 如果是动作类请求进来，会先在request.getParameter中获取
 *   页号(key_query_page_no)            默认主键为：pageNo
 *   每页记录数(key_query_page_size)    默认主键为：pageSize
 *   总记录数(key_query_all_count)      默认主键为：allCount
 * 的参数主键值
 * 
 * 然后先从request.getParameter中获取这三个值，
 * 如果不存在，则从线程会话ThreadSession中获取（主键名相同）
 * 如果仍然不存在：
 * pageNo 默认为 1 （当前页号）
 * pageSize 先从线程会话中获取query_default_page_size的值，如果仍然为空，则默认为20（每页记录数）
 * allCount 不存在的话，在执行查询时，会先执行查询总记录数的语句
 * 
 * 会自动从页面请求中获取提交的pageNo当前页号，和pageSize每页记录数的参数值
 * 
 * 2018-07-06 如果没有获取到分页信息，则设置分页信息默认值
 * 2018-11-08 如果传参中，每页记录数小于0，则不做分页处理。如果每页记录数为0，默认20条记录每页
 *            修改了获取分页数据错误
 * 2018-11-12 修改了获取分页信息报空指针错误
 * 2019-02-22 增加了线程参数query_no_fix_page，为1时做不分页查询
 * 2019-03-06 去掉了数据源信息，在获取数据库操作类时已经传入数据源主键，无需再这里设置
 * 2019-11-06 增加了操作表的表名变量，有些场景需要用到，比如返回的数据是放在临时表中的，需要知道临时表的表名
 *            增加了一些单独获取分页信息、每页记录数、是否分页的静态方法
 * 2020-07-02 增加了序列化方法
 * 2021-05-12 修改了构造函数中，处理页号值的错误
 * 2021-06-29 增加了几个存储信息的变量
 * 2021-11-26 在toString方法中补充了没有显示出来的变量
 * 2022-06-22 修改了一个方法中入参String[] 改为Object[]，不影响原先使用
 * 
 * @author 刘虻
 * 2011-6-2 下午02:58:52
 */
@ClassInfo({"2022-06-22 22:40","分页查询容器"})
public class QueryPageVO implements ISerializable {

  /**
   * 分页信息类
   */
  public String 
        countSql       = null         //获取总数语句
        ,sql           = null         //查询语句
        ,keyPageNo     = null         //从页面请求中获取当前页的参数主键
        ,keyPageSize   = null         //从页面请求中获取每页记录数的参数主键
        ,keyAllCount   = null         //从页面请求中获取总记录数的参数主键
        ,sourceKey     = null         //数据源主键（该值实际上没有直接作用于选择数据源，仅作信息存储）
        ,errorMsg      = null;        //错误信息（该值仅作信息存储）
  
  public int 
        pageNo         = 0            //页号  起始1
        ,pageSize      = 0            //每页记录数
        ,fromId        = 0            //起始序号  起始1
        ,toId          = 0            //结束序号
        ,pageCount     = 0            //总页数
        ,allCount      = 0            //总记录数
        ,status        = 1;           //操作状态（该值不参与数据库操作，仅作特殊情况信息存储）

  // 仅用作保存信息的容器（不参与任何数据库操作）
  public Map<String,Object> attributeMap = new HashMap<>();
  
  public String tableName = null;     //该语句操作的表名
  
  public boolean noFixPage  = false;  //是否不进行分页处理
  
  public Object[] sqlObj = null;     //适用于脚本中 <%S  S%> 拼装语句
  
  @SuppressWarnings("rawtypes")
  public List uList   = new ArrayList();  //提交参数序列
  
  public Object[] uParas = null; //提交值（Object方式，为了兼顾PostgreSQL）
  
  public Object[] dicts = null; //数据字典序列
  
  public List<String> fields = new ArrayList<String>(); //字段名序列
    
  public List<Map<String,String>> rs = new ArrayList<Map<String,String>>();  //记录集
  
  /**
   * 构造函数
   * @author 刘虻
   */
  public QueryPageVO() {
    super();
    
    //注意：不能直接获取到参数值直接给转换成了整型，然后用是否小于0来判断是否为有效值
    //因为每页记录数可以传入小于等于0的数值，作为不分页处理
    
    //从页面请求中获取分页控制信息(如果存在的话)
    IActionContext ac = (IActionContext)ThreadSession.get(IServletConst.KEY_ACTION_CONTEXT);
    keyPageNo   = ""; //页号参数值主键
    keyPageSize = ""; //每页记录数主键
    keyAllCount = ""; //总记录数主键
    
    //从页面请求中获取值
    String pageNoStr   = "";
    String pageSizeStr = "";
    
    if(ac!=null) {
        //查询页号参数主键
        keyPageNo = ac.getParameter("key_query_page_no");
        if(keyPageNo.length()<1) {
          keyPageNo = SString.valueOf(ThreadSession.get("key_query_page_no"));
        }
        //查询每页记录数参数主键
        keyPageSize = ac.getParameter("key_query_page_size");
        if(keyPageSize.length()<1) {
            keyPageSize = SString.valueOf(ThreadSession.get("key_query_page_size"));
        }
        //获取总记录数（如果页面提交了总记录数，就不再查询总记录数）
        keyAllCount = ac.getParameter("key_query_all_count");
        if(keyAllCount.length()<1) {
          keyAllCount = SString.valueOf(ThreadSession.get("key_query_all_count"));
        }
    }
    //如果没有从页面请求中获取到该值，则从线程容器中获取
    if(keyPageNo.length()<1) {
      keyPageNo = SString.valueOf(ThreadSession.get("key_query_page_no"));
    }
    if(keyPageNo.length()<1) {
      keyPageNo = "pageNo";
    }

    if(keyPageSize.length()<1) {
      keyPageSize = SString.valueOf(ThreadSession.get("key_query_page_size"));
    }
    if(keyPageSize.length()<1) {
      keyPageSize = "pageSize";
    }
    if(ac!=null){
      pageNoStr = ac.getParameter(keyPageNo);
    }
    if(pageNoStr.length()<1) {
      pageNoStr = SString.valueOf(ThreadSession.get(keyPageNo));
    }
    pageNo = SInteger.valueOf(pageNoStr);
    if(pageNo<1) {
      pageNo = 1;
    }

    if(ac!=null){
      pageSizeStr = ac.getParameter(keyPageSize);
    }
    if(pageSizeStr.length()<1){
      pageSizeStr = SString.valueOf(ThreadSession.get(keyPageSize));
      if(pageSizeStr.length()<1){
        pageSizeStr = SString.valueOf(ThreadSession.get("query_default_page_size"));
      }
    }
    pageSize = SInteger.valueOf(pageSizeStr);
    if(pageSize==0){
      pageSize = 20;
    }else if(pageSize<0){
      noFixPage = true;
    }
    if(!noFixPage){
      //是否不分页（在查询之前不查询记录总数量）
      String noFixPageStr = SString.valueOf(ThreadSession.get("query_no_fix_page"));
      if("1".equals(noFixPageStr)) {
        noFixPage = true;
      }
    }
    if(!noFixPage && keyAllCount.length()>0){
      if(keyAllCount.length()<1) {
        keyAllCount = "allCount";
      }
      allCount = SInteger.valueOf(ac.getParameter(keyAllCount)); 
    }
  }
  
  /**
   * 当前页号（从请求对象中获取）
   * @return   当前页号字符串
   * 2019年11月6日
   * @author MBG
   */
  public static int pageNo() {
	 int pNo = SInteger.valueOf(pageNoStr());
	 if(pNo<1) {
		 pNo = 1;
	 }
	 return pNo;
  }
  
  
  /**
   * 当前页号（从请求对象中获取）
   * @return   当前页号字符串
   * 2019年11月6日
   * @author MBG
   */
  public static String pageNoStr() {
	    //注意：不能直接获取到参数值直接给转换成了整型，然后用是否小于0来判断是否为有效值
	    //因为每页记录数可以传入小于等于0的数值，作为不分页处理
	    
	    //从页面请求中获取分页控制信息(如果存在的话)
	    IActionContext ac = 
	        (IActionContext)ThreadSession.get(IServletConst.KEY_ACTION_CONTEXT);
	    
	  return pageNoStr(ac);
  }
  
  /**
   * 当前页号（从请求对象中获取）
   * @param ac 动作上下文
   * @return   当前页号字符串
   * 2019年11月6日
   * @author MBG
   */
  public static String pageNoStr(IActionContext ac) {
	  if(ac==null) {
		  return "";
	  }
	  String keyPageNo = ac.getParameter("key_query_page_no");
      if(keyPageNo.length()<1) {
      	keyPageNo = SString.valueOf(ThreadSession.get("key_query_page_no"));
          if(keyPageNo.length()<1) {
              keyPageNo = "pageNo";
          }
      }
      return ac.getParameter(keyPageNo);
  }
  
  /**
   * 是否做分页查询标识
   * @return 是否做分页查询标识
   * 2019年11月6日
   * @author MBG
   */
  public static boolean noFixPage() {
	  return SBoolean.valueOf(noFixPageStr());
  }
  
  /**
   * 是否做分页查询标识
   * @return 是否做分页查询标识
   * 2019年11月6日
   * @author MBG
   */
  public static String noFixPageStr() {
	    //从页面请求中获取分页控制信息(如果存在的话)
	    IActionContext ac = 
	        (IActionContext)ThreadSession.get(IServletConst.KEY_ACTION_CONTEXT);
	     return noFixPageStr(ac);
  }
  
  /**
   * 是否做分页查询标识
   * @param ac 动作上下文
   * @return 是否做分页查询标识
   * 2019年11月6日
   * @author MBG
   */
  public static String noFixPageStr(IActionContext ac) {
	  if(ac==null) {
		  return "";
	  }
	  String noFixPage = ac.getParameter("query_no_fix_page");
      if(noFixPage.length()<1) {
    	  noFixPage = SString.valueOf(ThreadSession.get("query_no_fix_page"));
      }
      if(noFixPage.length()<1) {
    	  return "0";
      }
      return noFixPage;
  }
  
  /**
   * 每页记录数（从请求对象中获取）
   * @return   每页记录数字符串
   * 2019年11月6日
   * @author MBG
   */
  public static int pageSize() {
	 int pSize = SInteger.valueOf(pageSizeStr());
	 if(pSize<1) {
		 pSize = 20;
	 }
	 return pSize;
  }
  
  /**
   * 每页记录数（从请求对象中获取）
   * @return   每页记录数字符串
   * 2019年11月6日
   * @author MBG
   */
  public static String pageSizeStr() {
	    //注意：不能直接获取到参数值直接给转换成了整型，然后用是否小于0来判断是否为有效值
	    //因为每页记录数可以传入小于等于0的数值，作为不分页处理
	    
	    //从页面请求中获取分页控制信息(如果存在的话)
	    IActionContext ac = 
	        (IActionContext)ThreadSession.get(IServletConst.KEY_ACTION_CONTEXT);
	    
	  return pageSizeStr(ac);
  }
  
  /**
   * 每页记录数（从请求对象中获取）
   * @param ac 动作上下文
   * @return   每页记录数字符串
   * 2019年11月6日
   * @author MBG
   */
  public static String pageSizeStr(IActionContext ac) {
	  if(ac==null) {
		  return "";
	  }
	  String keyPageSize = ac.getParameter("key_query_page_size");
      if(keyPageSize.length()<1) {
    	  keyPageSize = SString.valueOf(ThreadSession.get("key_query_page_size"));
          if(keyPageSize.length()<1) {
        	  keyPageSize = "pageSize";
          }
      }
      return ac.getParameter(keyPageSize);
  }
  
  /**
   * 设置提交值数组
   * @param arrl 提交值数组
   * @return 当前类实例
   * 2015年4月10日
   * @author 马宝刚
   */
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public QueryPageVO updates(Object[] arrl) {
     uList = new ArrayList();
     if(arrl!=null && arrl.length>0) {
         for(int i=0;i<arrl.length;i++) {
             uList.add(arrl[i]);
         }
     }
     return this;
  }
  
  /**
   * 覆盖方法
   */
  @Override
  public String toString() {
      return "QueryPageVO::: countSql:["+countSql+"] sql:["+sql+"] keyPageNo:["+keyPageNo+"] keyPageSize:["
                  +keyPageSize+"] pageNo:["+pageNo+"] pageSize:["+pageSize+"] fromId:["
                  +fromId+"] toId:["+toId+"] pageCount:["+pageCount+"] allCount:["+allCount+"] uList("+(uList==null?0:uList.size())+"):["
                  +DebugUtil.getListValue(uList," ")+"] fields("+(fields==null?0:fields.size())+"):["
                  +DebugUtil.getListValue(fields," ")+"] uParas:["+DebugUtil.getArrayString(uParas)+"]";
    }



  /**
   * 将当前类实例进行序列化
   * 
   * 注意：只能将分页记录集远程传递过去，不能远程发起查询请求
   * 
   * @return 序列化的数据字符串
   */
  @Override
  public String serialize() {
	  //构建返回值
	  StringBuffer res = new StringBuffer();
	  
	  String nullStr = new String(new byte[] {0x00}); //空对象标识
	  String brStr   = new String(new byte[] {0x09}); //值分隔符
	  String eObjStr = new String(new byte[] {0x03}); //值中的元素结束分隔符
	  String keyStr  = new String(new byte[] {0x02}); //key value 分隔符
	  String ekeyStr = new String(new byte[] {0x01}); //keyvalue 元素之间分隔符
	  String ele;                                     //循环体元素
	  String val;                                     //值元素
	  Map<String,String> eleMap;                      //行记录集元素
	  
	  res
	    .append(countSql==null?nullStr:countSql).append(brStr)           //00    countSql
	    .append(sql==null?nullStr:sql).append(brStr)                     //01    sql
	    .append(keyPageNo==null?nullStr:keyPageNo).append(brStr)         //02    keyPageNo
	    .append(keyPageSize==null?nullStr:keyPageSize).append(brStr)     //03    keyPageSize
	    .append(pageNo).append(brStr)                                    //04    pageNo
	    .append(pageSize).append(brStr)                                  //05    pageSize
	    .append(fromId).append(brStr)                                    //06    fromId
	    .append(toId).append(brStr)                                      //07    toId
	    .append(pageCount).append(brStr)                                 //08    pageCount
	    .append(allCount).append(brStr)                                  //09    allCount
	    .append(tableName==null?nullStr:tableName).append(brStr)         //10    tableName
	    .append(noFixPage?"1":"0").append(brStr);                        //11    noFixPage
	    
	    
	  if(fields.size()>0) {                                              //12    fields
		  for(int i=0;i<fields.size();i++) {
			  if(i>0) {
				  res.append(eObjStr);
			  }
			  ele = fields.get(i);
			  res.append(ele==null?nullStr:ele);
		  }
	  }else if(rs.size()>0){
		  eleMap = rs.get(0);
		  if(eleMap!=null) {
			  //记录集字段名迭代
			  Iterator<String> keys = eleMap.keySet().iterator();
			  if(keys!=null) {
				  while(keys.hasNext()) {
					  fields.add(keys.next());
				  }
			  }
		  }
	  }
	  res.append(brStr);
	  
	  
	  if(rs.size()>0) {                                                   //13   rs
		  for(int i=0;i<rs.size();i++) {
			  if(i>0) {
				  res.append(eObjStr);
			  }
			  eleMap = rs.get(i);
			  for(int j=0;j<fields.size();j++) {
				  if(j>0) {
					  res.append(ekeyStr);
				  }
				  ele = fields.get(j);
				  val = eleMap.get(ele);
				  res.append(ele).append(keyStr).append(val==null?nullStr:val);
			  }
		  }
	  }
	  return res.toString();
  }
  


  /**
   * 将序列化字符串反向成类实例
   * 
   * 注意：只能将分页记录集远程传递过去，不能远程发起查询请求
   * 
   * @param data 序列化数据字符串
   * @return 对应的类实例
   */
  @Override
  public Object unserialize(String data) {
	  
	  String brStr   = new String(new byte[] {0x09}); //值分隔符
	  
	  //变量值数组
	  String[] datas = data.split(brStr);
	  if(datas==null || datas.length<14) {
		  //非法数据
		  return this;
	  }
	  
	  int    index   = 0;                             //值索引
	  String nullStr = new String(new byte[] {0x00}); //空对象标识
	  String eObjStr = new String(new byte[] {0x03}); //值中的元素结束分隔符
	  String keyStr  = new String(new byte[] {0x02}); //key value 分隔符
	  String ekeyStr = new String(new byte[] {0x01}); //keyvalue 元素之间分隔符
	  
	  //00   countSql
	  if(!nullStr.equals(datas[index])) {
		  countSql = datas[index];
	  }else {
		  countSql = null;
	  }
	  
	  //01   sql
	  index++;
	  if(!nullStr.equals(datas[index])) {
		  sql = datas[index];
	  }else {
		  sql = null;
	  }
	  
	  //02   keyPageNo
	  index++;
	  if(!nullStr.equals(datas[index])) {
		  keyPageNo = datas[index];
	  }else {
		  keyPageNo = null;
	  }
	  
	  //03   keyPageSize
	  index++;
	  if(!nullStr.equals(datas[index])) {
		  keyPageSize = datas[index];
	  }else {
		  keyPageSize = null;
	  }
	  
	  //04   pageNo
	  index++;
	  pageNo = SInteger.valueOf(datas[index]);
	  
	  //05   pageSize
	  index++;
	  pageSize = SInteger.valueOf(datas[index]);
	  
	  //06   fromId
	  index++;
	  fromId = SInteger.valueOf(datas[index]);
	  
	  //07   toId
	  index++;
	  toId = SInteger.valueOf(datas[index]);
	  
	  //08   pageCount
	  index++;
	  pageCount = SInteger.valueOf(datas[index]);
	  
	  //09   allCount
	  index++;
	  allCount = SInteger.valueOf(datas[index]);
	  
	  //10   tableName
	  index++;
	  if(!nullStr.equals(datas[index])) {
		  tableName = datas[index];
	  }else {
		  tableName = null;
	  }
	  
	  //11   noFixPage
	  index++;
	  if("1".equals(datas[index])) {
		  noFixPage = true;
	  }else {
		  noFixPage = false;
	  }
	  
	  String[] cDatas; //子元素数组
	  
	  //12  fields
	  index++;
	  fields = new ArrayList<String>();
	  if(datas[index].length()>0) {
		  cDatas = datas[index].split(eObjStr);
		  if(cDatas!=null && cDatas.length>0) {
			  for(int i=0;i<cDatas.length;i++) {
				  if(nullStr.equals(cDatas[i])) {
					  fields.add(null);
				  }else {
					  fields.add(cDatas[i]);
				  }
			  }
		  }
	  }
	  
	  //13  rs
	  index++;
	  rs = new ArrayList<>();
	  if(datas[index].length()>0) {
		  Map<String,String> colMap; //行记录
		  cDatas = datas[index].split(eObjStr);
		  if(cDatas!=null && cDatas.length>0) {
			  String[] ccDatas; //子元素中的元素
			  String[] kvDatas; //key value值
			  for(int i=0;i<cDatas.length;i++) {
				  colMap  = new HashMap<>();
				  rs.add(colMap);
				  ccDatas = cDatas[i].split(ekeyStr);
				  if(ccDatas!=null && ccDatas.length>0) {
					  for(int j=0;j<ccDatas.length;j++) {
						  //key value 元素
						  kvDatas = ccDatas[j].split(keyStr);
						  if(kvDatas!=null && kvDatas.length>1) {
							  if(nullStr.equals(kvDatas[1])) {
								  kvDatas[1] = null;
							  }
							  colMap.put(kvDatas[0],kvDatas[1]);
						  }
					  }
				  }
			  }
		  }
	  }
	  
	  return this;
  }
}
